#include <cstddef>
#include <grpcpp/create_channel.h>
#include <grpcpp/impl/codegen/client_context.h>
#include <grpcpp/impl/codegen/status.h>
#include <grpcpp/security/credentials.h>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <memory>
#include <atomic>
#include <queue>
#include <string>
#include <grpcpp/grpcpp.h>
#include "../generated/hello.pb.h"
#include "../generated/hello.grpc.pb.h"
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using hello::Greeter;
using hello::HelloReply;
using hello::HelloRequest;

//封装一个Stub-Channel连接池
class RPConPool
{
  public:
      RPConPool(std::size_t poolSize,const std::string& host,const std::string& port)
      :_poolSize(poolSize),_host(host),_port(port)
      {
         //预先创建出一批连接
         for(int i = 0 ; i < _poolSize ;i++)
         {
            auto channel = grpc::CreateChannel(_host+":"+_port, grpc::InsecureChannelCredentials());
            _connections.push(Greeter::NewStub(channel));
            //Greeter::NewStub(channel)返回的是一个右值,此时push内部
            //调用的是value_type的移动构造
         }
      }

      void Close()
      {
          //将标记位置为true
          _stop = true;
          //唤醒所有条件变量上等的线程，告诉他们连接池不工作了
          _cv.notify_all();
      }


     ~RPConPool()
     {

     }

     //这是一个 按值传参，但因为是右值，
     // 所以调用的是 std::unique_ptr 的 移动构造函数，而不是拷贝构造函数。
     void returnConnection(std::unique_ptr<Greeter::Stub> conn)
     {
         std::unique_lock<std::mutex> lock(_mtx);
         if(_stop) return;
         //放回队列 唤醒在条件变量上等的线程
         _connections.push(std::move(conn));
         _cv.notify_one();
     }

     std::unique_ptr<Greeter::Stub> getConnection()
     {
          std::unique_lock<std::mutex> lock(_mtx);
          //连接池停止 || 队列中有空闲连接才继续往下走
          _cv.wait(lock,[&](){
              return _stop || !_connections.empty();
          });
          //如果是连接池停止工作则直接退出
          if(_stop) return nullptr;
          auto stb= std::move(_connections.front());
          _connections.pop();
          //在 C++11 之后，如果函数返回一个局部对象（如stb），编译器会自动尝试用 移动构造（不是拷贝构造）来生成返回值。
          return stb;
     }
     //mutable 是 C++ 的一个 关键字，用来 打破 const 限制，让某个成员变量即使在 const 成员函数 里也能被修改。
     size_t Size()  const
     {
         //const成员函数mtx是const std::mutex _mtx;
         //std::unique_lock<std::mutex> 的构造函数需要std::mutex&  // 非 const 引用
         //解决方案是mutable
         std::unique_lock<std::mutex> lock(_mtx);
         return _connections.size();
     }

  private:
    std::atomic<bool> _stop;//连接池是否停止工作标志
    size_t _poolSize; //连接池的数量
    mutable  std::mutex _mtx; //互斥锁
    std::condition_variable _cv;
    std::queue<std::unique_ptr<Greeter::Stub>> _connections; //连接池队列
    //如果你的连接池语义是“借用-归还”，推荐继续用 unique_ptr；
    // 如果 Stub 需要在多个线程或异步任务间共享使用，再换成 shared_ptr。
    //ip + 端口
    std::string _host;
    std::string _port; 
};
// 使用 shared_ptr 后，无法区分：
// a.谁在用；
// b.谁该还；
// c.连接是否真的空闲。
// 而使用 unique_ptr，这些语义非常清晰：
// a.取出即所有权转移；
// b.归还必须显式 std::move；
// c.编译期就能防止错误的复制（即双持资源）。


//Greeter Client类
class GreeterClient
{
public:
   GreeterClient(size_t poolSize,const std::string& host,const std::string& port)
   :_pool(poolSize, host, port)
   {}

   size_t Size()
   {
     return _pool.Size();
   }

   HelloReply Hello(std::string name)
   {
        HelloRequest req;
        req.set_name(name);
        HelloReply rsp;
        ClientContext ctx;
        //
        auto stub = _pool.getConnection();
        Status status = stub->SayHello(&ctx, req, &rsp);
        if(status.ok())
           return rsp;
        std::cerr << "RPC failed : " << status.error_message() << std::endl;
        //归还连接
        _pool.returnConnection(std::move(stub)); //这里一定是move()
        return rsp;
   }

private:
   RPConPool _pool;
   
};

int main()
{
  GreeterClient cli(4,"127.0.0.1","50051");
  HelloReply resp1 = cli.Hello("zqh");
  std::cout << resp1.message() << std::endl;
  HelloReply resp2 = cli.Hello("zz");
  std::cout << resp2.message() << std::endl;

  return 0;
}



// stub 和 channel 是一一绑定的；
// 多个 Stub 可以共享同一个 Channel；
// Channel 本身是线程安全的，可同时用于多个 RPC 调用。

int main1() {
  // 创建到服务端的 channel（演示用明文）  信道
  //// 它是到服务端的“连接抽象”，封装了 HTTP/2 连接池、重试、负载均衡（按配置）等。
  auto channel = grpc::CreateChannel("127.0.0.1:50051",
                                     grpc::InsecureChannelCredentials());
  // 生成的客户端 Stub，像“远程接口代理” 类似信使
  /// 这是 protoc 生成的“远程代理”，里面的每个方法都对应 .proto 里的每个 rpc。
  std::unique_ptr<Greeter::Stub> stub = Greeter::NewStub(channel);

  HelloRequest req;
  req.set_name("zqh");

  HelloReply resp;
  ClientContext ctx;  // 可设置 metadata/超时等

  Status status = stub->SayHello(&ctx, req, &resp);
    // 1) 将 req 序列化为 protobuf 二进制
    // 2) 通过 gRPC/HTTP2 发到路径 /hello.Greeter/SayHello
    // 3) 收到响应体后反序列化到 resp
    // 4) 返回传输层状态给你检查

  if (!status.ok()) {
    std::cerr << "RPC failed: " << status.error_message() << std::endl;
    return 1;
  }

  std::cout << "🟢 Reply: " << resp.message() << std::endl;
  return 0;
}